'From Smalltalk-80, Version 2.3 of 13 June 1988 on 4 September 1991 at 8:32:24 am'!
!Plan methodsFor: 'interpretation'!
execute
"Execute my constraints in order."
Sensor leftShiftDown ifTrue: [Transcript show: '*** executing plan ***'; cr].
historyVariables do: [:v | v advanceHistory].
constraints do:
[:c |
c execute.
Sensor leftShiftDown ifTrue: [Transcript show: c printString; cr]]! !
!Planner class methodsFor: 'planning/value propagation'!
makePlan: seedConstraints
"Extract a plan for resatisfaction starting from the given seed constraints, usually a set of input constraints. This method assumes that stay optimization is desired; the plan will contain only constraints whose output variables are not stay. Constraints that do no computation, such as stay and edit constraints, are not included in the plan."
"Details: The outputs of a constraint are marked when it is added to the plan under construction. A constraint may be appended to the plan when all its input variables are known. A variable is known if either a) the variable is marked (indicating that has been computed by a constraint appearing earlier in the plan), b) the variable is 'stay' (i.e. it is a constant at plan execution time), or c) the variable is not determined by any constraint. The last provision is for past states of history variables, which are not stay but which are also not computed by any constraint."
"Assume: seedConstraints are all satisfied."
| todo mark plan hotC out inC |
todo _ seedConstraints.
mark _ self newMark.
plan _ Plan new.
(todo isEmpty) ifFalse: [hotC _ todo removeFirst].
[hotC == nil] whileFalse:
[((hotC output mark ~= mark) and: "not in plan already and..."
[hotC inputsKnown: mark]) "eligible for inclusion"
ifTrue:
[
hotC output stay ifFalse: [
plan append: hotC.
].
out _ hotC output.
out mark: mark.
hotC _ self nextConstraintIn: todo downstreamOf: out]
ifFalse:
[(hotC output mark ~= mark) ifTrue:
["this code backs up in the constraint graph; this is useful when not all the source nodes are easily determined"
Just as a database demo is not complete with the Employee relation and a graphics demo is not complete without rendering a teapot, a constraint demo is not complete with the Celsius-Fahrenheit example.
Constraints are multi-directional, automatically-maintained assertions about the state of a system. For example, the relation between Celsius and Fahrenheit temperatures is a constraint. Constraints are often stated as equations, but other mechanism are both possible and are used in these demos.
This demo uses many different types of constraints: arithmetic constraints to maintain the relation between the different interpretations of temperature; layout constraints to keep the title under each thermometer, the equations between the thermometers, and the number beside the mercury; and consistency constraints to keep the mercury height proportional to the temperature which in turn is equal to the text printed beside it. Last, but not least, special graphical constraints are used to control the display when the temperature gets too cold or too hot (Try 10 Kelvin or 200 Celsius!!). Yes, the entire demo is composed of many smaller objects working together, synchronized and kept consistent with constraints.
Interesting actions to try are: (1) moving the mercury up and down, (2) selecting and then entering a temperature directly (use <delete> and the number keys), (3) switching to "edit" mode and rearranging the thermometers (press the mouse button on the title bar to get a menu which includes "edit". Use the same menu to return to "operate").
'!
!CFKDemo methodsFor: 'initialize-release'!
create
| c f k c1 tc tf tk tcf tck mpc |
Transcript cr; show: 'Building the ', self class name, '..'.
Transcript cr; show: '..adding the Fahrenheit thermometer'.
f _ ThermometerGlyph new initialize.
f moveTo: 50 @ 160.
f overVal: 752.0 underVal: -184.0.
self addGlyph: f.
Transcript cr; show: '..adding the Celsius thermometer'.
c _ ThermometerGlyph new initialize.
c moveTo: 250 @ 160.
c overVal: 400.0 underVal: -120.0.
self addGlyph: c.
Transcript cr; show: '..adding the Kelvin thermometer'.
k _ ThermometerGlyph new initialize.
k moveTo: 450 @ 160.
k minVal: 0.
k maxVal: 400.
k overVal: 450.0 underVal: -1.0.
self addGlyph: k.
Transcript cr; show: '..adding the consistency constraints'.
' ***** What is a Constraint Hierarchy? (Anchors and Mice) *****
A constraint hierarchy is an ordered sequence of sets of constraints such that the constraints in the stronger sets dominate those in the weaker sets. For example, if "X = 5" is strong and "X = 3" is weak, the solution would be "X = 5". The strongest set in a constraint hierarchy is the required level: these constraints must be satisified. All other levels are preferred and should be satisfied (respecting their various strengths) may be violated if necessary.
These three examples demonstrate different constraint hierarchies. In the no-anchor example, the only constraints are the "horizontal line" constraint and (when the mouse drags a point) the "mouse drags point" constraint. (Note: all interactions are implemented using constraints, thus the connection between the mouse and the point it is dragging is actually a constraint.) In the anchor example, there is a strong "anchor" constraint which holds the left point in place. This anchor constraint is stronger than the mouse constraint, and thus the mouse cannot move the left end and it can only move the right end back and forth. In the anchor-strong-mouse example, the mouse has been made stronger than the anchor and thus the mouse can drag the anchor around again.
To summarize, the three examples have the following constraint hierarchies:
* no-anchor * * anchor * * anchor-strong-mouse *
required horizontal line required horizontal line required horizontal line
' ***** Why use Constraint Hierarchies? (Sums) *****
Constraint hierarchies are useful for many parts of a software system including: (i) as a declarative specification for defaults, (ii) as a mechanism to describe the behavior of a graphical user interface, and (iii) as a mechanism for declaratively controlling the dataflow. Typically, to control the flow of data in a "flat" constraint system one must use some operational features. For example, many constraint system use a well-defined search mechanism and thus the user can write his or her rules to take advantage of that mechanism to control the dataflow, i.e., he or she can use non-declarative features. Of course, if the system is improved and the search algorithm changes, the program no longer works correctly. One of the benefits of using a constraint hierarchy is that the hierarchy can control the dataflow declaratively.
The no-hierarchy demo has a flat constraint system and just a little experimentation will illustrate that its behavior is difficult to predict. Specifically, the data does not flow from left-to-right as one might expect. The hierarchy demo uses constraints of different strengths to cause the "correct" dataflow. Furthermore, any constraint hierarchy solver will produce exactly the same solutions for this second demo, regardless of its implementation (assuming, of course, that the solver is implemented correctly.)
'!
!Plus1Demo methodsFor: 'initialize-release'!
create
"Plus1Demo releaseConstraints"
| texts xs ys ps |
Transcript cr; show: 'Building the ' , self class name , '..'.
Transcript cr; show: '..adding the components'.
values _ Array new: 7.
1 to: values size do: [:i | values at: i put: (FreeVariable value: 10)].
texts _ Array new: values size.
1 to: values size do: [:i | texts at: i put: BoxTextGlyph new].
xs _ #(50 50 150 150 250 250 350 ).
ys _ #(50 150 100 200 150 250 200 ).
ps _ OrderedCollection new.
xs with: ys do: [:x :y | ps add: x @ y].
texts with: ps do:
[:text :p |
text moveTo: p].
xs _ #(100 200 300 ).
ys _ #(100 150 200 ).
1
to: 5
by: 2
do:
[:i |
p _ ThreeProngTextGlyph new initialize.
p
left: (texts at: i) center
right: (texts at: i + 2) center
down: (texts at: i + 1) center
string: '+'.
self addGlyph: p].
texts do: [:each | self addGlyph: each].
Transcript cr; show: '..adding the consistency constraints'.
' ***** Complex Graphical User Interfaces (MacDraw II) *****
This demo was taken from the MacDraw II Dashed Lines dialog box, a user interface widget for defining the number and length of the black and white dashes that, together, comprise a dashed line. In the original MacDraw II, this dialog box was implemented completely in Pascal: the programmer designed the box, extracted the constraints, hand-solved them, coded them in Pascal, and debugged and debugged and debugged. In this version, the behavior is almost completely defined by constraints. Even the existence or non-existence of the dashes is defined by constraints.
This dialog box uses three basic types of constraints: data consistency constraints, graphical constraints, and behavioral constraints. Data consistency constraints include: no dash shall be shorted than 5 pixels. No dash shall be longer than 125 pixels. There must be at least two dashes. There can be no more than six dashes. Etc. Graphical constraints include keeping the "draggers" aligned with the right end of their respective dash expect for the last one which can be dragged to the far right edge to delete a dash. Behavioral constraints are basically internal but include such things as the existence of dashes and draggers, and "snapping" action which occurs when the last dragger is released between the right end of its dash and the right edge of the box.
Interesting actions to try include: (1) trying to shrink a dash too far, (2) trying to grow a dash too large, (3) dragging the last dragger to the far right, (4) dragging the dragger on the far right (if there you left it there) off the far right "parking" place, (5) trying to compact a dash on the right by shoving it against the edge with a dash from the middle, and (6) releasing the last dragger half-way between the end of its dash and the right edge of the box.
'!
!MacDrawDemo methodsFor: 'initialize-release'!
create1
| c d |
Transcript cr; show: 'Building the ' , self class name , '..'.
Transcript cr; show: '..adding the dashes'.
dashes _ d _ Array new: 6.
1 to: d size do: [:i | d at: i put: (MacDrawDashGlyph new initialize: i)].
(d at: 1) left0 value: MacDrawDemo leftEdge.
2 to: d size + 1 do:
[:i |
(d at: i - 1) right0 value: (d at: i - 1) left0 value + (d at: i - 1) length0 value.
(d at: i - 1) right1 value: (d at: i - 1) left1 value + (d at: i - 1) length1 value.
(d at: i - 1) right2 value: (d at: i - 1) left2 value + (d at: i - 1) length2 value.
(d at: i - 1) right3 value: (d at: i - 1) left3 value + (d at: i - 1) length3 value.
"Note: replace the previous code with the following to disable click, drag, and sweep gestures in 'operate' mode. This is good for naive users and quick response."
!MacDrawDashGlyph class methodsFor: 'classification'!
glyphCategory
^'SIGGRAPH'! !
!MacDrawDashGlyph class methodsFor: 'constraint release'!
releaseConstraints
"MacDrawDashGlyph releaseConstraints"
LengthConstraint _ nil! !
Scene subclass: #SplittingDemo
instanceVariableNames: 'fakemouse '
classVariableNames: 'RandomStream '
poolDictionaries: ''
category: 'Minstrel-SIGGRAPH'!
SplittingDemo comment:
' ***** Constraint on Objects (Multi-User Application) *****
These demos illustrate two features of constraint systems: (one) constraints can be useful in the development of today''s multi-user, multi-media, multi-buzzword applications, and (two) constraints on complex objects can be implemented in a number of ways. These demos illustrate two mechanisms: splitting (decomposing a constraint on a whole object into separate constraints on each of its parts) and raising (moving constraints on an object''s parts "up" to become constraints on the whole object). Numerous other mechanisms also exist.
In these examples, we assume a two-user, two-mouse interactive graphical editor. Here, however, the second user is simulated by the computer. Note that even while the second user is busy editting her objects, the first user (you) can still edit yours. Again, this is because all interactions are implemented with constraints and the system can solve many constraints just as easily as it can solve a few.
Each of these demos has a horizontal line and a vertical line. The first user (you) are supposed to drag the right end of the horizontal line and the second user to drag the bottom end of the vertical line. This works well in the splitting demo. In the raising demo, however, the system has raised the constraints too far and has removed precious degrees of freedom from the objects. Thus the constraints (you, the second user, horizontal, vertical) cannot all be solved simultaneously.
'!
!SplittingDemo methodsFor: 'initialize-release'!
create
| hl vl m c |
Transcript cr; show: 'Building the ' , self class name , '..'.
Transcript cr; show: '..adding the components'.
m _ FakeMouseGlyph new initialize.
hl _ LineGlyph new.
hl moveTo: 100 @ 100.
vl _ LineGlyph new.
vl moveTo: 100 @ 150.
self addGlyph: m; addGlyph: hl; addGlyph: vl.
Transcript cr; show: '..adding the consistency constraints'.
!SIGGRAPHAnchorGlyph class methodsFor: 'classification'!
glyphCategory
^'SIGGRAPH'! !
SIGGRAPHAnchorGlyph initialize!
Scene subclass: #ThreePlanetDemo
instanceVariableNames: 'theta1 r1 theta2 r2 '
classVariableNames: ''
poolDictionaries: ''
category: 'Minstrel-SIGGRAPH'!
ThreePlanetDemo comment:
' ***** Animation (Orbiting Planetoids) *****
Constraints are especially useful for two aspects of graphics: static layout and dynamic behavior. Constraints can be used to declaratively specify the layout of various graphical objects to ensure that positioning and other relations are maintained. Perhaps more interesting, though, is the use of constraints to define dynamic behavior, e.g., animations. Constraints are a natural mechanism for describing physical laws (even imaginary "Wiley E. Coyote" ones).
These two demos illustrate the use of simple animation constraints. In the fake-gravity demo, the slides control the orbit radius of the two planets and simple computation is used to find the next position of each planet. Interestingly enough, the animation can continue while the user is interacting with the system---after all, the user''s interaction is implemented by constraints and the animation is implemented by constraints, thus the system can automatically solve the two sets together. Thus the user can move the slider while the planets orbit. However, the user can also grab any planet and drag it around and the other planets will continue their orbits, although now centered on the user''s planet. This "re-centering" is because the user''s interaction constraint is stronger than the rest.
In the real-gravity demo, the slider control gravity and the four vectors illustrate the acceleration and velocity of the two planets. All of the constraints are multi-directional, thus if the planets are dragged around, the vectors will indicate the velocity and acceleration of the mouse!! And, if the velocity vector is dragged around, the acceleration and planet motion will match, and so on. Unfortunately, it is very difficult to get the planets to orbit (or to do anything at all) which is why the fake-gravity demo is also supplied.